#include "./rf_uart.h"
#include "../device/gpio.h"
#include "../device/uart.h"
#include "../device/dma.h"


namespace driver {

using namespace device::gpio;
using namespace device::uart;
using namespace device::dma;

using TX = PA<2>; // DMA1_CH7
using RX = PA<3>; // DMA1_CH6

using TX_DMA = Dma<DMA1_BASE, 7>;
using TX_DMA_CHAN = DmaChan<DMA1_Channel7_BASE>;

using RX_DMA = Dma<DMA1_BASE, 6>;
using RX_DMA_CHAN = DmaChan<DMA1_Channel6_BASE>;

static constexpr uint32_t MyBrr = 36000000 / 115200;

using MY_UART = Uart<USART2_BASE, MyBrr, TX, RX, TX_DMA, TX_DMA_CHAN, RX_DMA, RX_DMA_CHAN>;


RfUart::RfUart() {

    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
    RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
    RCC->AHBENR |= RCC_AHBENR_DMA1EN;

    notify_write_complete();

    MY_UART::init(m_recv_buf, RECV_BUF_SIZE);

    NVIC_EnableIRQ(USART2_IRQn);
    NVIC_EnableIRQ(DMA1_Channel7_IRQn);
}

void RfUart::write_data(uint8_t cmd, uint16_t arg_len) {
    using namespace utils;
    
    m_write_buf[0] = HEAD;
    m_write_buf[1] = TYPE_REQ;
    m_write_buf[2] = cmd;
    encode<uint16_t>(m_write_buf + 3, arg_len);

    uint8_t s = utils::alg::sum_add(m_write_buf + 1, arg_len + 4);
    m_write_buf[5 + arg_len] = s;
    m_write_buf[6 + arg_len] = END;

    write_by_dma(m_write_buf, 7 + arg_len);
}

void RfUart::write_by_dma(const void *buf, uint8_t len) {
    MY_UART::write(buf, len);
} 

void RfUart::on_recv_idle() {
    uint32_t index = RECV_BUF_SIZE - RX_DMA_CHAN::dntr();
    if (index == m_recv_index) {
        return;
    }
    utils::ByteView bw;
    if (index > m_recv_index) {
        bw.buf = m_recv_buf + m_recv_index;
        bw.len = index - m_recv_index;
        m_mq.post(bw);

    } else {
        bw.buf = m_recv_buf + m_recv_index;
        bw.len = RECV_BUF_SIZE - m_recv_index;
        m_mq.post(bw);

        bw.buf = m_recv_buf;
        bw.len = index;
        m_mq.post(bw);
    }
    m_recv_index = index;
}

}

using namespace driver;

extern "C"
void USART2_IRQHandler() {
    if (!MY_UART::is_idle()) {
        return;
    }
    MY_UART::clear_idle_flag();
    rfUart->on_recv_idle();
}

extern "C"
void DMA1_Channel7_IRQHandler() {
    if (!TX_DMA::is_complete()) {
        return;
    }
    TX_DMA::clear_flags();
    rfUart->notify_write_complete();
}
